#!/bin/sh
':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"

// Core node modules
var path = require('path');
var os = require('os');
var zlib = require('zlib');
var child_process = require('child_process');

// 3rd party modules
var fs = require('fs-extra');
var getopt = require('node-getopt');
var tar = require('tar');

function make_error_cb(message)
{
   return function(e) {
      console.error("Error %s: %s", message, e);
      process.exit(1);
   };
}

function remove_files(options)
{
   if (options.verbose)
      console.log("Removing temporary files");

   fs.remove(options.project_folder);
}

function run_project(options)
{
   if (options.verbose)
      console.log("Running the project");

   var current_dir = process.cwd();
   process.chdir(options.project_root);

   var proc = child_process.fork(options.metadata.Entry);
   proc.on('exit', function(code){
      if (options.verbose)
         console.log('Child exited with code %s', code);
      process.chdir(current_dir);
      if (!options.keep)
         remove_files(options);
   });

}

function unpack_project_data(options)
{
   if (options.verbose)
      console.log("Unpacking project sources and assets");

   var datafile = path.join(options.project_folder, "data.tar.gz");
   var project_root = path.join(options.project_folder, "root");

   options.project_root = project_root;

   var input = fs.createReadStream(datafile);
   var unzipper = zlib.createGunzip();
   var extractor = tar.Extract({path: project_root, strip: 0});

   input.on('error', make_error_cb("reading package data file."));
   extractor.on('error', make_error_cb("unpacking package data file."));
   if (!("only-extract" in options))
      extractor.on('end', function(){ run_project(options); });

   input.pipe(unzipper)
   unzipper.pipe(extractor);
}

function read_metadata(options)
{
   if (options.verbose)
      console.log("Reading project metadata");

   var project_folder = options.project_folder;
   var metadata = JSON.parse(fs.readFileSync(path.join(project_folder, "meta.json")));

   if (options.verbose)
      console.log("Project: %s\nVersion: %s\nEntry point: %s", metadata.Name, metadata.Version, metadata.Entry);
   if ("only-dump" in options)
      process.exit(0);

   options.metadata = metadata;

   unpack_project_data(options);
}

function extract(filename, options)
{
   if (options.verbose)
      console.log("Extracting ", filename, "with options ", options);

   var project_id = path.basename(filename, ".epk");
   var project_folder = path.join(options['temp-dir'], project_id);

   options.project_folder = project_folder;
   options.project_id = project_id;

   var input = fs.createReadStream(filename);
   var extractor = tar.Extract({path: options['temp-dir'], strip: 0});

   input.on('error', make_error_cb("reading package file."));
   extractor.on('error', make_error_cb("unpacking package file."));
   extractor.on('end', function(){ read_metadata(options); });

   input.pipe(extractor);
}

function main() {
   var options = getopt.create([
       ['d', 'only-dump', 'Only dump information about the package'],
       ['e', 'only-extract', 'Only extract the package, do not run'],
       ['h', 'help', 'Display this help'],
       ['k', 'keep', 'Do not remove the files after exiting'],
       ['t', 'temp-dir=ARG', 'Temporary dir to extract files'],
       ['v', 'verbose', 'Print information messages'],
   ]).bindHelp().parseSystem();

   var filename = options.argv[0];
   if (filename === undefined)
     {
        console.error("Must provide a package file.");
        process.exit(1);
     }

   if (!('temp-dir' in options.options))
     {
        options.options["temp-dir"] = path.join(os.tmpdir(), "efljs_apps");
        if (options.verbose)
         console.log("Defaulting temp dir to ", options.options["temp-dir"]);
     }

   extract(filename, options.options);
}

main();
